#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <unistd.h>

/* prevents "unused parameter" warning by the compiler */
#define UNUSED(x) ((void)(x))
#define NUM_DUMB_THREADS 5

/* flag to tell threads when to return, 
 * many threads will read it, only the "director" thread
 * write to it, no synchronization is needed */
int threads_must_return = 0;

/* to be runned by the "dumb" threads, just
 * keep the thread running until threads_must_return
 * is true */
void *
do_nothing(void * foo)
{
   unsigned int r;

   UNUSED(foo);

   while (1) {
      if (threads_must_return)
         return NULL; /* pthread_exit can confuse valgrind, use return instead */
      r = sleep((unsigned int) 1);
      if (r != 0) {
         fprintf(stderr, "sleep call was interrupted by a signal handler\n");
         exit(EXIT_FAILURE);
      }
      printf("dumb thread heartbeat\n");
      fflush(stdout);
   }
}

/* to be runned by the "director" thread, wait for a key
 * press to set the threads_must_return flag */
void *
read_stdin(void * foo)
{
   char c;
   ssize_t nread;

   UNUSED(foo);

   printf("Press any key to quit\n");

   nread = read(0, (void *) &c, sizeof(char));
   if (nread == -1) {
      perror("read");
      exit(EXIT_FAILURE);
   }

   threads_must_return = 1;
   printf("Key press detected, wait until program ends\n");
   fflush(stdout);

   return NULL;
}

int
main(int argc, char ** argv)
{
   pthread_attr_t thread_attr;
   pthread_t thread_director;
   pthread_t thread_dumb[NUM_DUMB_THREADS];
   int r;
   int i;

   UNUSED(argc); 
   UNUSED(argv);

   /* create the attributes to use when creating the threads */
   /* the same attribute will be reused in the creation of all threads */
   r = pthread_attr_init(&thread_attr);
   if (r != 0) {
      perror("ptrhead_attr_init");
      exit(EXIT_FAILURE);
   }
   /* redundant: Joinable is set by default */
   r = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
   if (r != 0) {
      perror("ptrhead_attr_setdetachstate");
      exit(EXIT_FAILURE);
   }

   /* launch director thread */
   r = pthread_create(&thread_director, &thread_attr, &read_stdin, NULL);
   if (r != 0) {
      perror("pthread_create");
      exit(EXIT_FAILURE);
   }

   /* launch some dumb threads */
   for (i=0; i<NUM_DUMB_THREADS; i++) {
      r = pthread_create(&thread_dumb[i], &thread_attr, &do_nothing, NULL);
      if (r != 0) {
         perror("pthread_create");
         exit(EXIT_FAILURE);
      }
   }

   /* destroy the thread attributes */
   r = pthread_attr_destroy(&thread_attr);

   /* wait until all threads are finnished */
   r = pthread_join(thread_director, NULL);
   if (r != 0) {
      perror("pthread_join");
      exit(EXIT_FAILURE);
   }
   for (i=0; i<NUM_DUMB_THREADS; i++) {
      r = pthread_join(thread_dumb[i], NULL);
      if (r != 0) {
         perror("pthread_join");
         exit(EXIT_FAILURE);
      }
   }

   /* exit normally */
   exit(EXIT_SUCCESS);
}
